/*-------------------------------------------------------------------------
 *
 * bool.c
 *	  用于内置类型“bool”的函数。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/utils/adt/bool.c
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"

#include <ctype.h>

#include "libpq/pqformat.h"
#include "utils/builtins.h"

/*
 * 尝试将值解释为布尔值。有效值为： true,
 * false, yes, no, on, off, 1, 0；以及它们的唯一前缀。
 * 如果字符串解析正常，则返回 true，否则返回 false。
 * 如果正常且结果不为 NULL，则在 *result 中返回该值。
 */
bool parse_bool(const char *fc_value, bool *fc_result)
{
	return parse_bool_with_len(fc_value, strlen(fc_value), fc_result);
}

bool parse_bool_with_len(const char *fc_value, size_t fc_len, bool *fc_result)
{
	switch (*fc_value)
	{
		case 't':
		case 'T':
			if (pg_strncasecmp(fc_value, "true", fc_len) == 0)
			{
				if (fc_result)
					*fc_result = true;
				return true;
			}
			break;
		case 'f':
		case 'F':
			if (pg_strncasecmp(fc_value, "false", fc_len) == 0)
			{
				if (fc_result)
					*fc_result = false;
				return true;
			}
			break;
		case 'y':
		case 'Y':
			if (pg_strncasecmp(fc_value, "yes", fc_len) == 0)
			{
				if (fc_result)
					*fc_result = true;
				return true;
			}
			break;
		case 'n':
		case 'N':
			if (pg_strncasecmp(fc_value, "no", fc_len) == 0)
			{
				if (fc_result)
					*fc_result = false;
				return true;
			}
			break;
		case 'o':
		case 'O':
			/* 'o' 不够唯一 */
			if (pg_strncasecmp(fc_value, "on", (fc_len > 2 ? fc_len : 2)) == 0)
			{
				if (fc_result)
					*fc_result = true;
				return true;
			}
			else if (pg_strncasecmp(fc_value, "off", (fc_len > 2 ? fc_len : 2)) == 0)
			{
				if (fc_result)
					*fc_result = false;
				return true;
			}
			break;
		case '1':
			if (fc_len == 1)
			{
				if (fc_result)
					*fc_result = true;
				return true;
			}
			break;
		case '0':
			if (fc_len == 1)
			{
				if (fc_result)
					*fc_result = false;
				return true;
			}
			break;
		default:
			break;
	}

	if (fc_result)
		*fc_result = false;		/* 抑制编译器警告 */
	return false;
}

/*****************************************************************************
 *	 用户 I/O 例程														 *
 *****************************************************************************/

/*
 *		boolin			- 将 "t" 或 "f" 转换为 1 或 0
 *
 * 明确检查 "true/false" 和 TRUE/FALSE，1/0，YES/NO，ON/OFF。
 * 拒绝其他值。
 *
 * 在 switch 语句中，首先检查最常用的可能性。
 */
Datum boolin(PG_FUNCTION_ARGS)
{
	const char *fc_in_str = PG_GETARG_CSTRING(0);
	const char *fc_str;
	size_t		fc_len;
	bool		fc_result;

	/*
	 * 跳过前导和尾随空格
	 */
	fc_str = fc_in_str;
	while (isspace((unsigned char) *fc_str))
		fc_str++;

	fc_len = strlen(fc_str);
	while (fc_len > 0 && isspace((unsigned char) fc_str[fc_len - 1]))
		fc_len--;

	if (parse_bool_with_len(fc_str, fc_len, &fc_result))
		PG_RETURN_BOOL(fc_result);

	ereport(ERROR,
			(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
			 errmsg("invalid input syntax for type %s: \"%s\"",
					"boolean", fc_in_str)));

	/* 未到达 */
	PG_RETURN_BOOL(false);
}

/*
 *		boolout			- 将 1 或 0 转换为 "t" 或 "f"
 */
Datum boolout(PG_FUNCTION_ARGS)
{
	bool		fc_b = PG_GETARG_BOOL(0);
	char	   *fc_result = (char *) palloc(2);

	fc_result[0] = (fc_b) ? 't' : 'f';
	fc_result[1] = '\0';
	PG_RETURN_CSTRING(fc_result);
}

/*
 *		boolrecv			- 将外部二进制格式转换为布尔值
 *
 * 外部表示是一个字节。任何非零值都视为 "true"。
 */
Datum boolrecv(PG_FUNCTION_ARGS)
{
	StringInfo	fc_buf = (StringInfo) PG_GETARG_POINTER(0);
	int			fc_ext;

	fc_ext = pq_getmsgbyte(fc_buf);
	PG_RETURN_BOOL(fc_ext != 0);
}

/*
 *		boolsend			- 将布尔值转换为二进制格式
 */
Datum boolsend(PG_FUNCTION_ARGS)
{
	bool		fc_arg1 = PG_GETARG_BOOL(0);
	StringInfoData fc_buf;

	pq_begintypsend(&fc_buf);
	pq_sendbyte(&fc_buf, fc_arg1 ? 1 : 0);
	PG_RETURN_BYTEA_P(pq_endtypsend(&fc_buf));
}

/*
 *		booltext			- 布尔值到文本的转换函数
 *
 * 我们需要这个，因为它的行为与 boolout() 不同；
 * 该函数遵循 SQL 规范的结果（除了产生小写字母）。
 */
Datum booltext(PG_FUNCTION_ARGS)
{
	bool		fc_arg1 = PG_GETARG_BOOL(0);
	const char *fc_str;

	if (fc_arg1)
		fc_str = "true";
	else
		fc_str = "false";

	PG_RETURN_TEXT_P(cstring_to_text(fc_str));
}


/*****************************************************************************
 *	 公共例程														 *
 *****************************************************************************/

Datum booleq(PG_FUNCTION_ARGS)
{
	bool		fc_arg1 = PG_GETARG_BOOL(0);
	bool		fc_arg2 = PG_GETARG_BOOL(1);

	PG_RETURN_BOOL(fc_arg1 == fc_arg2);
}

Datum boolne(PG_FUNCTION_ARGS)
{
	bool		fc_arg1 = PG_GETARG_BOOL(0);
	bool		fc_arg2 = PG_GETARG_BOOL(1);

	PG_RETURN_BOOL(fc_arg1 != fc_arg2);
}

Datum boollt(PG_FUNCTION_ARGS)
{
	bool		fc_arg1 = PG_GETARG_BOOL(0);
	bool		fc_arg2 = PG_GETARG_BOOL(1);

	PG_RETURN_BOOL(fc_arg1 < fc_arg2);
}

Datum boolgt(PG_FUNCTION_ARGS)
{
	bool		fc_arg1 = PG_GETARG_BOOL(0);
	bool		fc_arg2 = PG_GETARG_BOOL(1);

	PG_RETURN_BOOL(fc_arg1 > fc_arg2);
}

Datum boolle(PG_FUNCTION_ARGS)
{
	bool		fc_arg1 = PG_GETARG_BOOL(0);
	bool		fc_arg2 = PG_GETARG_BOOL(1);

	PG_RETURN_BOOL(fc_arg1 <= fc_arg2);
}

Datum boolge(PG_FUNCTION_ARGS)
{
	bool		fc_arg1 = PG_GETARG_BOOL(0);
	bool		fc_arg2 = PG_GETARG_BOOL(1);

	PG_RETURN_BOOL(fc_arg1 >= fc_arg2);
}

/*
 * 布尔与和布尔或聚合。
 */

/*
 * 标准 EVERY 聚合的函数，符合 SQL 2003。
 * 该聚合也称为 bool_and，以保持一致性。
 *
 * 注意：这仅在普通聚合模式下使用，而不是移动聚合模式。
 */
Datum booland_statefunc(PG_FUNCTION_ARGS)
{
	PG_RETURN_BOOL(PG_GETARG_BOOL(0) && PG_GETARG_BOOL(1));
}

/*
 * 标准 ANY/SOME 聚合的函数，符合 SQL 2003。
 * 该聚合称为 bool_or，因为 ANY/SOME 有解析冲突。
 *
 * 注意：这仅在普通聚合模式下使用，而不是移动聚合模式。
 */
Datum boolor_statefunc(PG_FUNCTION_ARGS)
{
	PG_RETURN_BOOL(PG_GETARG_BOOL(0) || PG_GETARG_BOOL(1));
}

typedef struct BoolAggState
{
	int64		aggcount;		/* 聚合的非空值数量 */
	int64		aggtrue;		/* 聚合中为真的值的数量 */
} BoolAggState;

static BoolAggState *
fc_makeBoolAggState(FunctionCallInfo fcinfo)
{
	BoolAggState *fc_state;
	MemoryContext fc_agg_context;

	if (!AggCheckCallContext(fcinfo, &fc_agg_context))
		elog(ERROR, "aggregate function called in non-aggregate context");

	fc_state = (BoolAggState *) MemoryContextAlloc(fc_agg_context,
												sizeof(BoolAggState));
	fc_state->aggcount = 0;
	fc_state->aggtrue = 0;

	return fc_state;
}

Datum bool_accum(PG_FUNCTION_ARGS)
{
	BoolAggState *fc_state;

	fc_state = PG_ARGISNULL(0) ? NULL : (BoolAggState *) PG_GETARG_POINTER(0);

	/* 首次调用时创建状态数据 */
	if (fc_state == NULL)
		fc_state = fc_makeBoolAggState(fcinfo);

	if (!PG_ARGISNULL(1))
	{
		fc_state->aggcount++;
		if (PG_GETARG_BOOL(1))
			fc_state->aggtrue++;
	}

	PG_RETURN_POINTER(fc_state);
}

Datum bool_accum_inv(PG_FUNCTION_ARGS)
{
	BoolAggState *fc_state;

	fc_state = PG_ARGISNULL(0) ? NULL : (BoolAggState *) PG_GETARG_POINTER(0);

	/* bool_accum 应该已经创建状态数据 */
	if (fc_state == NULL)
		elog(ERROR, "bool_accum_inv called with NULL state");

	if (!PG_ARGISNULL(1))
	{
		fc_state->aggcount--;
		if (PG_GETARG_BOOL(1))
			fc_state->aggtrue--;
	}

	PG_RETURN_POINTER(fc_state);
}

Datum bool_alltrue(PG_FUNCTION_ARGS)
{
	BoolAggState *fc_state;

	fc_state = PG_ARGISNULL(0) ? NULL : (BoolAggState *) PG_GETARG_POINTER(0);

	/* 如果没有非空值，返回 NULL */
	if (fc_state == NULL || fc_state->aggcount == 0)
		PG_RETURN_NULL();

	/* 如果所有非空值都为真，返回真 */
	PG_RETURN_BOOL(fc_state->aggtrue == fc_state->aggcount);
}

Datum bool_anytrue(PG_FUNCTION_ARGS)
{
	BoolAggState *fc_state;

	fc_state = PG_ARGISNULL(0) ? NULL : (BoolAggState *) PG_GETARG_POINTER(0);

	/* 如果没有非空值，返回 NULL */
	if (fc_state == NULL || fc_state->aggcount == 0)
		PG_RETURN_NULL();

	/* 如果任何非空值为真，返回真 */
	PG_RETURN_BOOL(fc_state->aggtrue > 0);
}
